#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <unordered_map> // hash

#define COMMAND_SIZE 1024

// 命令行格式
#define FORMAT "[%s@%s %s]# "

// 下面是Shell定义的全局数据

#define MAXARGC 128
#define MAXENVP 100

// 命令行参数/参数表
char *g_argv[MAXARGC];
int g_argc = 0;

// 环境变量表
char *g_env[MAXENVP];
int g_envs = 0; // 环境变量的个数

// 别名映射表
std::unordered_map<std::string, std::string> alias_list;

// last exit code
int lastcode = 0;

// for test
char cwd[1024];
char cwdenv[1024];

//关于重定向，我们关系的内容
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

// #define TrimSpace(CMD, index) do {
//     while (isspace(*(CMD+index)))
//     {
//         index++;
//     }
// } while(0)



int redir = NONE_REDIR;
std::string filename;

// last_cwd 用于记录上一次的工作目录
char last_cwd[1024] = {0};

// 获取用户名
const char *GetUserName() {
    const char *name = getenv("USER");
    return name == NULL ? "None" : name;
}

// 获取主机名
const char *GetHostName() {
    const char *hostname = getenv("HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

// 获取当前路径
const char *GetPwd() {
    const char *pwd = getcwd(cwd, sizeof(cwd));
    if (pwd != nullptr) {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", pwd);
        putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;
}

// 获取家目录
const char *GetHome() {
    const char *home = getenv("HOME");
    return home == NULL ? "None" : home;
}

// 获取环境变量表
void InitEnv() {
    extern char **environ;
    memset(g_env, 0, sizeof(g_env));

    // 从父Shell获取环境变量
    for (int i = 0; environ[i]; i++) {
        g_env[i] = strdup(environ[i]); // 使用 strdup 复制字符串
        g_envs++;
    }
    g_env[g_envs++] = strdup("HAHA=for_test"); // 添加一个测试环境变量
    g_env[g_envs] = NULL;

    // 将环境变量表赋给 environ
    environ = g_env;
}

// cd 命令
bool Cd() {
    if (g_argc == 1) {
        // 如果没有指定路径，默认切换到家目录
        std::string home = GetHome();
        if (home.empty()) {
            std::cerr << "cd: HOME not set" << std::endl;
            return false;
        }
        chdir(home.c_str());
    } else {
        std::string where = g_argv[1];
        if (where == "~") {
            // 切换到家目录
            std::string home = GetHome();
            if (home.empty()) {
                std::cerr << "cd: HOME not set" << std::endl;
                return false;
            }
            chdir(home.c_str());
        } else if (where == "-") {
            // 切换到上一次的工作目录
            if (last_cwd[0] != '\0') {
                chdir(last_cwd);
            } else {
                std::cerr << "cd: no previous directory saved" << std::endl;
                return false;
            }
        } else {
            // 切换到指定目录
            if (chdir(where.c_str()) != 0) {
                std::cerr << "cd: " << where << ": No such file or directory" << std::endl;
                return false;
            }
        }
    }

    // 更新 last_cwd 为当前目录
    getcwd(last_cwd, sizeof(last_cwd));
    return true;
}

// echo 命令
// echo 命令
bool Echo() {
    if (g_argc > 1) {
        std::string output;
        for (int i = 1; i < g_argc; ++i) {
            output += g_argv[i];
            output += " ";
        }
        output.pop_back(); // 去掉最后一个空格

        if (redir == OUTPUT_REDIR || redir == APPEND_REDIR) {
            int fd = open(filename.c_str(), redir == OUTPUT_REDIR ? O_CREAT | O_WRONLY | O_TRUNC : O_CREAT | O_WRONLY | O_APPEND, 0666);
            if (fd < 0) {
                std::cerr << "echo: failed to open file" << std::endl;
                return false;
            }
            write(fd, output.c_str(), output.size());
            close(fd);
        } else {
            std::cout << output << std::endl;
        }
    }
    return true;
}

// export 命令
bool Export() {
    if (g_argc < 2) {
        std::cerr << "export: not enough arguments" << std::endl;
        return true;
    }
    for (int i = 1; i < g_argc; ++i) {
        std::string env_str = g_argv[i];
        size_t equal_pos = env_str.find('=');
        if (equal_pos == std::string::npos) {
            std::cerr << "export: invalid variable name" << std::endl;
            return true;
        }
        std::string key = env_str.substr(0, equal_pos);
        std::string value = env_str.substr(equal_pos + 1);
        char *env_entry = new char[key.size() + value.size() + 2];
        sprintf(env_entry, "%s=%s", key.c_str(), value.c_str());
        putenv(env_entry);
        g_env[g_envs++] = env_entry;
    }
    return true;
}

// alias 命令
bool Alias() {
    if (g_argc == 1) {
        // 显示所有别名
        for (const auto &entry : alias_list) {
            std::cout << entry.first << "=" << entry.second << std::endl;
        }
    } else if (g_argc == 2) {
        // 删除别名
        std::string nickname = g_argv[1];
        if (alias_list.find(nickname) != alias_list.end()) {
            alias_list.erase(nickname);
        } else {
            std::cerr << "alias: " << nickname << ": not found" << std::endl;
        }
    } else if (g_argc == 3) {
        // 添加别名
        std::string nickname = g_argv[1];
        std::string target = g_argv[2];
        alias_list[nickname] = target;
    } else {
        std::cerr << "alias: invalid arguments" << std::endl;
    }
    return true;
}

// 获取当前路径的父目录
std::string DirName(const char *pwd) {
    std::string dir = pwd;
    if (dir == "/") return "/";
    auto pos = dir.rfind('/');
    if (pos == std::string::npos) return "/";
    return dir.substr(0, pos);
}

// 初始化命令行
bool MakeCommandLine(char cmd_prompt[], int size) {
    snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
    return true;
}

// 打印命令行提示符
void PrintCommandPrompt() {
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt, sizeof(prompt));
    printf("%s", prompt);
    fflush(stdout);
}

// 获取用户输入的命令
bool GetCommandLine(char *out, int size) {
    char *c = fgets(out, size, stdin);
    if (c == NULL) {
        return false;
    }
    out[strcspn(out, "\n")] = 0; // 去掉换行符
    if (strlen(out) == 0) {
        return false;
    }
    return true;
}

// 命令行分析
bool CommandParse(char *commandline) {
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline, " ");
    while ((g_argv[g_argc++] = strtok(NULL, " ")));
    g_argc--;

    // 检查别名
    std::string cmd = g_argv[0];
    auto it = alias_list.find(cmd);
    if (it != alias_list.end()) {
        // 如果是别名，替换为实际命令
        std::string new_cmd = it->second;
        delete[] g_argv[0]; // 释放旧的命令
        g_argv[0] = new char[new_cmd.size() + 1];
        strcpy(g_argv[0], new_cmd.c_str());
    }

    return g_argc > 0;
}

// 打印解析后的命令和参数
void PrintArgv() {
    for (int i = 0; i < g_argc; ++i) {
        printf("g_argv[%d]->%s\n", i, g_argv[i]);
    }
}

// 检测并处理内建命令
bool CheckAndExecBuiltin() {
    std::string cmd = g_argv[0];
    if (cmd == "cd") {
        return Cd();
    } else if (cmd == "echo") {
        return Echo();
    } else if (cmd == "export") {
        return Export();
    } else if (cmd == "alias") {
        return Alias();
    }
    return false;
}
// 执行命令
int Execute() {
    pid_t id = fork();
    if (id == 0) {
        int fd = -1;
        //子进程检查重定向情况
        if(redir == INPUT_REDIR)
        {
            fd = open(filename.c_str(), O_RDONLY);
            if(fd < 0) exit(1);
            dup2(fd,0);
            close(fd);
        }
        else if(redir == OUTPUT_REDIR)
        {
            fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_TRUNC,0666);
            if(fd < 0) exit(2);
            dup2(fd,1);
            close(fd);
        }
        else if(redir == APPEND_REDIR)
        {
            fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd < 0) exit(3);
            dup2(fd,1);
            close(fd);
        }
        else
        {
            //没有重定向  DO Nothing
        }

        //child
        execvp(g_argv[0], g_argv);
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0) {
        lastcode = WEXITSTATUS(status);
    }
    return 0;
}


//去空格的接口
void TrimSpace(char cmd[], int& end)
{
    while(isspace(cmd[end]))
    {
        end++;
    }
}

//重定向检查
void RedirCheck(char cmd[])
{
    //清空
    redir = NONE_REDIR;
    filename.clear();

    int start = 0;
    int end = strlen(cmd)-1;

    //"ls -a -l > file.txt"//> >> <
    //逆着来
    while(end > start)
    {
        if(cmd[end] == '<')
        {
            cmd[end++] = 0;
            TrimSpace(cmd,end);//end下标指向f
            redir = INPUT_REDIR;
            filename = cmd+end;
            break;
        }
        else if(cmd[end] == '>')
        {
            if(cmd[end-1] == '>')
            {
                cmd[end-1] = 0;
                redir = APPEND_REDIR;
            }
            else
            {
                redir = OUTPUT_REDIR;
            }
            cmd[end++] = 0;
            TrimSpace(cmd,end);//end下标指向f
            filename = cmd + end;
            break;
        }
        else
        {
            end--;
        }
    }
}

// 清理函数
void Clear() {
    for (int i = 0; i < g_envs; ++i) {
        free(g_env[i]);
    }
}

int main()
{

    //Shell启动的时候，从系统中获取环境变量
    InitEnv();

    while (1) //命令行不会停止，要不断获得用户输入
    {
        //1.输出命令行提示符
        PrintCommandPrompt();

        //2.获取用户输入的命令
        char commandline[COMMAND_SIZE];
        if (!GetCommandLine(commandline, COMMAND_SIZE))
        {
            continue; //输入有问题就重新输入
        }

        //additional_3.重定向分析"ls -a -l file.txt" -> "ls -a -l" "file.txt" -> 判定重定向方式
        //左半部分才是下面3要分析的
        RedirCheck(commandline);
        //测试
        //printf("redir :%d, filename: %s\n", redir, filename.c_str());

        //3.命令行分析"ls -a -l"->"ls" "-a" "-l"，未来要使用程序替换，这种形式的参数，方便调用！！！
        if(!CommandParse(commandline))
        {
            continue;//如果解析失败，不执行以下代码了，解析成功才可执行！！！
        }

        //sub_4.检测并处理内建命令
        if(CheckAndExecBuiltin())
        {
            continue;
        }

        //4.执行命令
        Execute();
    }
    //清理函数
    Clear();
    return 0;
}





// #include <iostream>
// #include <cstdio>
// #include <cstring>
// #include <cstdlib>
// #include <unistd.h>
// #include <sys/types.h>
// #include <sys/wait.h>
// #include <string>
// #include <unistd.h>
// #include <unordered_map>//hash

// #define COMMAND_SIZE 1024

// //命令行格式
// #define FORMAT "[%s@%s %s]# "

// //下面是Shell定义的全局数据

// #define MAXARGC 128
// #define MAXENVP 100

// //命令行参数/参数表
// char *g_argv[MAXARGC];
// int g_argc = 0;

// //环境变量表
// char *g_env[MAXENVP];
// int g_envs = 0;//环境变量的个数

// //别名映射表
// std::unordered_map<std::string, std::string> alias_list;

// //last exit code
// int lastcode = 0;

// //for test
// char cwd[1024];
// char cwdenv[1024];


// //获取用户名
// const char *GetUserName()
// {
//     const char *name = getenv("USER");
//     return name == NULL ? "None" : name;
// }

// //获取主机名
// const char *GetHostName()
// {   
//     const char* hostname = getenv("HOSTNAME");
//     return hostname == NULL ? "None" : hostname;
// }

// //获取当前路径
// const char *GetPwd()
// {
//     //const char* pwd = getenv("PWD");
//     const char* pwd = getcwd(cwd,sizeof(cwd));
//     if(pwd != nullptr)
//     {
//         snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",pwd);
//         putenv(cwdenv);
//     }
//     return pwd == NULL ? "None" : pwd;
// }

// //获取家目录
// const char*GetHome()
// {
//     const char* home = getenv("HOME");
//     return home == NULL ? "None" : home;
// }

// //获取环境变量表
// void InitEnv()
// {
//     extern char** environ;
//     memset(g_env,0,sizeof(g_env));

//     //本来要从配置文件来的，今天直接从父Shell来：
//     //1.获取环境变量
//     for(int i=0;environ[i];i++)
//     {
//         //1.1申请空间
//         g_env[i] = (char*)malloc(strlen(environ[i])+1);
//         strcpy(g_env[i],environ[i]);
//         g_envs++;
//     }
//     g_env[g_envs++] = "HAHA=for_test";
//     g_env[g_envs] = NULL;

//     //2.导成环境变量
//     for(int i=0;g_env[i];i++)
//     {
//         putenv(g_env[i]);
//     }
//     //environ是C语言提供的全局变量，这时候就可以指向父进程的环境变量表了，一旦fork之后，子进程就照样拿到全局变量environ，就可以把所有的环境变量给拿到了
//     environ = g_env;
// }

// //command
// bool Cd()
// {
//     if(g_argc == 1)
//         {
//             std::string home = GetHome();
//             if(home.empty()) return true;
//             chdir(home.c_str());
//         }
//         else
//         {
//             std::string where = g_argv[1];
//             if (where == "~") 
//             {
//                 std::string home = GetHome();
//                 if (!home.empty())
//                 {
//                     chdir(home.c_str());
//                 }
//             } 
//             else if (where == "-") {
//             // 这里需要记录上一次的工作目录
//             // 假设有一个全局变量 `char* last_cwd` 用于存储上一次的工作目录
//             if (last_cwd != nullptr)
//             {
//                 chdir(last_cwd);
//             }
// }
//             else
//             {
//                 chdir(where.c_str());
//             }
//         }
//         return true;
// }

// bool Echo()
// {
//     if(g_argc == 2)
//         {
//             //echo "hello world"
//             //echo $?
//             //echo $PATH
//             std::string opt = g_argv[1];
//             if(opt == "$?")
//             {
//                 std::cout<<lastcode<<std::endl;
//                 lastcode = 0;//这里要注意清0！！！
//             }
//             else if(opt[0] == '$')
//             {
//                 std::string env_name = opt.substr(1);//去掉第一个字符剩下的就是环境变量的名字
//                 const char *env_value = getenv(env_name.c_str());
//                 if(env_value)//不为空
//                 {
//                     std::cout<<env_value<<std::endl;
//                 }
//             }
//             else
//             {
//                 std::cout<<opt<<std::endl;
//             }
//         }
//         return true;
// }

// bool Export()
// {
//     if (g_argc < 2) {
//         std::cerr << "export: not enough arguments" << std::endl;
//         return true;
//     }
//     for (int i = 1; i < g_argc; ++i) {
//         std::string env_str = g_argv[i];
//         size_t equal_pos = env_str.find('=');
//         if (equal_pos == std::string::npos) {
//             std::cerr << "export: invalid variable name" << std::endl;
//             return true;
//         }
//         std::string key = env_str.substr(0, equal_pos);
//         std::string value = env_str.substr(equal_pos + 1);
//         char* env_entry = new char[key.size() + value.size() + 2];
//         sprintf(env_entry, "%s=%s", key.c_str(), value.c_str());
//         putenv(env_entry);
//         g_env[g_envs++] = env_entry; // 将新环境变量添加到环境变量表
//     }
//     return true;
// }

// bool Alias()
// {
//     if (g_argc < 3) {
//         std::cerr << "alias: not enough arguments" << std::endl;
//         return true;
//     }
//     std::string nickname = g_argv[1];
//     std::string target = g_argv[2];
//     alias_list[nickname] = target; // 将别名映射添加到哈希表
//     return true;
// }

// // / | /a/b/c
// std::string DirName(const char *pwd)
// {
// #define SLASH "/"
//     std::string dir = pwd;
//     if(dir == SLASH)return SLASH;
//     auto pos = dir.rfind(SLASH);//从后向前找
//     if(pos == std::string::npos)return "BUG?";
//     return dir.substr(pos);//+1是不想要体现"/"
// }

// //初始化命令行
// bool MakeCommandLine(char cmd_prompt[], int size)
// {
//     snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
//     //snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), GetPwd());

//     return true;
// }

// //打印命令行提示符
// void PrintCommandPrompt()
// {
//     char prompt[COMMAND_SIZE];
//     MakeCommandLine(prompt, sizeof(prompt));
//     printf("%s", prompt);
//     fflush(stdout);
// }

// //用户输入是否成功
// bool GetCommandLine(char *out, int size)
// {
//     //ls -a -l->"ls -a -l"
//     char *c = fgets(out, size, stdin);
//     if (c == NULL)
//     {
//         //失败
//         return false;
//     }
//     //获取成功
//     out[strlen(out) - 1] = 0;
//     if (strlen(out) == 0)
//     {
//         return false;
//     }
//     return true;
// }

// //命令行分析
// bool CommandParse(char *commandline)
// {
// #define SEP " "
//     g_argc = 0;
//     //"ls -a -l"->"ls" "-a" "-l"
//     g_argv[g_argc++] = strtok(commandline, SEP); // 截取了ls，strtok只能切一次，切成功的话，返回下一个字串的起始地址
//     while (g_argv[g_argc++] = strtok(NULL, SEP)); // 最后会切到NULL

//     // 修正：确保g_argc在最后一次分割后不递增
//     g_argc--;
   

//     return g_argc>0 ? true : false;
// }

// //打印解析后的命令和参数
// void PrintArgv()
// {
//      for (int i = 0; i < g_argc; ++i) {
//             printf("g_argv[%d]->%s\n", i, g_argv[i]);
//         }
// }

// //检测并处理内建命令
// bool CheckAndExecBuiltin()
// {
//     std::string cmd = g_argv[0];
//     if(cmd == "cd")
//     {
//         return Cd();
//     }
//     else if(cmd == "echo")
//     {
//         return Echo();
//     }
//     else if(cmd == "export")
//     {
//         //找不到新增
//         //找到了覆盖
//         return Export();
//     }
//     else if(cmd == "alias")
//     {
//         return Alias();
//     }
//     return false;
// }

// //执行命令
// int Execute()
// {
//     pid_t id = fork();//创建子进程
//     if(id == 0)
//     {
//         //child
//         execvp(g_argv[0],g_argv);
//         exit(1);
//     }
//     int status = 0;
//     //father
//     pid_t rid = waitpid(id,&status,0);
//     //(void)rid;//rid使用一下，让编译器不报警
//     if(rid > 0)
//     {
//         lastcode = WEXITSTATUS(status);//拿到退出码
//     }
//     return 0;
// }

// //别名检查
// void CheakAlias()
// {
//     // 检测别名
//     for (int i = 0; i < g_argc; ++i) {
//         auto it = alias_list.find(g_argv[i]);
//         if (it != alias_list.end()) {
//             // 替换别名为实际命令
//             delete[] g_argv[i];
//             g_argv[i] = new char[it->second.size() + 1];
//             strcpy(g_argv[i], it->second.c_str());
//         }
//     }
// }

// //clean
// void Clear() {
//     for (int i = 0; i < g_envs; ++i) {
//         free(g_env[i]);
//     }
// }
// int main()
// {
//     //Shell启动的时候，从系统中获取环境变量
//     //但是我们今天自己的shell是没有这一套的，没有从相关配置去读取，我做不到，因为那个配置文件是Shell脚本，这是要求我们新学一门语言的！！！
//     //我们的自主实现的shell的环境变量信息应该从父shell统一来
//     InitEnv();

//     while (1) //命令行不会停止，要不断获得用户输入
//     {
//         //1.输出命令行提示符
//         PrintCommandPrompt();

//         //2.获取用户输入的命令
//         char commandline[COMMAND_SIZE];
//         if (!GetCommandLine(commandline, COMMAND_SIZE))
//         {
//             continue; //输入有问题就重新输入
//         }
//         //printf("echo %s\n", commandline); //用于回显，测试我们写的有无bug

//         //3.命令行分析"ls -a -l"->"ls" "-a" "-l"，未来要使用程序替换，这种形式的参数，方便调用！！！
//         if(!CommandParse(commandline))
//         {
//             continue;//如果解析失败，不执行以下代码了，解析成功才可执行！！！
//         }

//         //PrintArgv();

//         //检测别名
//         // 检测别名
//         CheakAlias();

//         //sub_4.检测并处理内建命令
//         if(CheckAndExecBuiltin())
//         {
//             continue;
//         }

//         //4.执行命令
//         Execute();
//     }
//     //清理函数
//     Clear();
//     return 0;
// }


